//
// Created by YANHAI on 2019/9/1.
//

/**
  C++中实现多态的原理
     1. 当类中声明虚函数时，编译器会在类中生成一个虚函数表
     2. 虚函数表示一个存储成员变量指针的数据结构
     3. 虚函数表示由编译器自动生成与维护的
     4. virtual成员函数会被编译器放入虚函数表中
     5. 存在虚函数时，每个对象中都有一个指向虚函数表的指针 vptr指针


  面试题3： 谈谈你对多态的理解。
            1. 多态实现的效果
               多态：同样的调用语句有多中不同的表现形式，一个函数在子类穿梭的时候表现不同的形态
            2. 多态实现的3个条件
               有继承， 有virtual重写，有父类指针（引用）指向子类对象
            3. 多态的C++实现
               通过virtual关键字告诉编译器是动态联编还是静态联编
            4.多态的理论基础
               动态联编 是在运行的时候才能确定是调用谁的函数
               静态联编 是在编译阶段确定如何执行这个函数
            5. 多态的意义
               多态是设计模式的基础 ，是框架的基石。
            6. 实现多态的理论基础
               函数指针做函数参数
               C函数指针是C++至高无上的荣耀，C函数指针一般有两种用法（正、反）
            7. 多态原理探究
                提前布局vptr指针，找到虚函数表，找到函数的入口地址

  面试题4：谈谈C++编译器是如何实现多态的？
            从原理上讲，上面的5条

  面试题5：是否可以将类的每个成员函数都声明为虚函数？
            可以，但不建议。
            1. 通过虚函数表指针vptr调用重写函数是在程序运行时进行的，因此需要通过寻址操作
                才能确定真正应该调用的函数。而普通成员函数是在编译时就确定了调用的函数，
                在效率上，虚函数的效率要低很多。
            2. 出于效率考虑，没有必要将所有成员函数都声明为虚函数。

*/


#include <iostream>

using std::cout;
using std::endl;

class Parent {
public:
    Parent(int a = 0)
    {
        this->a = a;
    }

    // 1. 编译器动手脚，写了virtual关键字会特殊处理
    //    虚函数表
    virtual void print()
    {
        cout << "Parent print()" << endl;
    }

private:
    int a;
};

class Child : public Parent {
public:
    Child(int a = 1, int b = 2) : Parent(a)
    {
        this->b = b;
    }

    virtual void print()
    {
        cout << "Child print()" << endl;
    }

private:
    int b;
};

void HowToPlay(Parent *base)
{
    // 2. 编译器动手脚，
    //   处理效果： 传来子类对象 执行子类的print函数
    //              传来父类对象 执行父类的print函数
    base->print();
    // 编译器根本不需要区分是子类对象还是父类对象
    // 父类对象和子类对象分别有vptr指针  ==> 虚函数表 ==> 函数的入口地址
    // 迟绑定（运行时才去判断）

}

int main()
{
    Parent p1;  // 3 编辑器动手脚，提前布局，
    //  用类定义对象的时候，编译器会在对象中添加一个vptr指针
    Child c1;   // 子类对象里面也有一个vptr指针

    HowToPlay(&p1);
    HowToPlay(&c1);
    return 0;
}
